package com.enbatis.dynamic.datasource.autoconfigure;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.enbatis.dynamic.datasource.autoconfigure.dialect.C3P0DS;
import com.enbatis.dynamic.datasource.autoconfigure.dialect.DBCPDS;
import com.enbatis.dynamic.datasource.autoconfigure.dialect.DruidDS;
import com.enbatis.dynamic.datasource.autoconfigure.dialect.xa.MysqlXAD;
import com.enbatis.dynamic.datasource.autoconfigure.dialect.xa.OracleXAD;
import com.enbatis.dynamic.datasource.autoconfigure.dialect.xa.PgsqlXAD;
import com.enbatis.dynamic.datasource.autoconfigure.dynamic.ConstantData;
import com.enbatis.dynamic.datasource.autoconfigure.dynamic.annotation.MybatisMapper;
import com.enbatis.dynamic.datasource.autoconfigure.dynamic.datasource.DynamicDatasource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author wwd
 * @date 2021-10-01 11:20
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.datasource.dynamic", name = "name")
@EnableConfigurationProperties({DynamicDatasourceProperties.class})
public class DynamicDatasourceAutoConfiguration implements InitializingBean {
    private final DynamicDatasourceProperties properties;

    public DynamicDatasourceAutoConfiguration(DynamicDatasourceProperties properties) {
        this.properties = properties;
    }

    @Bean
    @ConditionalOnProperty(
            name = {"jta.enabled"},
            havingValue = "true",
            matchIfMissing = false
    )
    @Primary
    public DynamicDatasource dynamicDataSources() throws Exception {
        Map<Object, Object> targetDataSources = new HashMap<>(16);
        String dynamicName = properties.getDynamicName();
        String defaultName = properties.getDefault();
        AtomikosDataSourceBean defaultDatasource = null;
        if (StringUtils.isNotBlank(dynamicName)) {
            String[] names = dynamicName.split(",");
            for (String name : names) {
                SqlProperty sqlProperty = new SqlProperty(properties, name);
                if (ConstantData.MYSQL.equals(sqlProperty.getDataType())) {
                    targetDataSources.put(name, MysqlXAD.initMysqlXA(sqlProperty));
                } else if (ConstantData.POSTGRESQL.equals(sqlProperty.getDataType())) {
                    targetDataSources.put(name, PgsqlXAD.initPgsqlXA(sqlProperty));
                } else if (ConstantData.ORACLE.equals(sqlProperty.getDataType())) {
                    targetDataSources.put(name, OracleXAD.initOracleXA(sqlProperty));
                }

                if (StringUtils.isNotBlank(defaultName) && name.equals(defaultName)) {
                    defaultDatasource = (AtomikosDataSourceBean) targetDataSources.get(name);
                }
            }
            if (null == defaultDatasource) {
                defaultDatasource = (AtomikosDataSourceBean) targetDataSources.get(names[0]);
            }
        }
        DynamicDatasource dynamicDatasource = new DynamicDatasource(defaultDatasource, targetDataSources);
        return dynamicDatasource;
    }


    @Bean
    @ConditionalOnProperty(
            name = {"jta.enabled"},
            havingValue = "false",
            matchIfMissing = true
    )
    @Primary
    public DynamicDatasource dynamicDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>(16);
        String dynamicName = properties.getDynamicName();
        String defaultName = properties.getDefault();
        DataSource defaultDatasource = null;
        if (StringUtils.isNotBlank(dynamicName)) {
            String[] names = dynamicName.split(",");
            for (String name : names) {
                SqlProperty sqlProperty = new SqlProperty(properties, name);
                if (ConstantData.DS_DRUID.equals(sqlProperty.getSourceType())) {
                    targetDataSources.put(name, DruidDS.initDruidDs(sqlProperty));
                } else if (ConstantData.DS_C3P0.equals(sqlProperty.getSourceType())) {
                    targetDataSources.put(name, C3P0DS.initC3p0Ds(sqlProperty));
                } else if (ConstantData.DS_DBCP.equals(sqlProperty.getSourceType())) {
                    targetDataSources.put(name, DBCPDS.initDbcpDs(sqlProperty));
                }

                if (StringUtils.isNotBlank(defaultName) && name.equals(defaultName)) {
                    defaultDatasource = (DataSource) targetDataSources.get(name);
                }
            }
            if (null == defaultDatasource) {
                defaultDatasource = (DataSource) targetDataSources.get(names[0]);
            }
        }
        DynamicDatasource dynamicDatasource = new DynamicDatasource(defaultDatasource, targetDataSources);
        return dynamicDatasource;
    }

    @Bean({"transactionManager"})
    @ConditionalOnProperty(
            name = {"jta.enabled"},
            havingValue = "false",
            matchIfMissing = true
    )
    public PlatformTransactionManager transactionManager() {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dynamicDataSource());
        dataSourceTransactionManager.setDefaultTimeout(1800);
        return dataSourceTransactionManager;
    }

    @Bean
    public Advisor dynamicDataSourceAdvisor() {
        Pointcut pointcut = new AnnotationMatchingPointcut(MybatisMapper.class, true);
        return new DefaultPointcutAdvisor(pointcut, new MethodAroundAdvice());
    }


    @Override
    public void afterPropertiesSet() throws Exception {

    }
}
